from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.orm import Session
from api.database import get_db
from api.model.user import User
from api.controller.auth import (
    get_current_user, authenticate_user, create_access_token,
    verify_password, verify_verification_token, invalidate_verification_token
)
from api.schemas.webauthn import (
    WebAuthnRegisterBeginRequest, WebAuthnRegisterBeginResponse,
    WebAuthnRegisterCompleteRequest, WebAuthnLoginBeginRequest,
    WebAuthnLoginBeginResponse, WebAuthnLoginCompleteRequest,
    WebAuthnUnbindRequest
)
from api.schemas.auth import VerifyPasswordRequest
from api.utils.response import success, fail
from datetime import datetime, timedelta
import secrets
import base64
import json
import pyotp

router = APIRouter()

# 临时存储challenge（生产环境应使用Redis等缓存）
_challenges = {}

def generate_challenge():
    """生成随机challenge"""
    return base64.urlsafe_b64encode(secrets.token_bytes(32)).decode('utf-8').rstrip('=')

def get_rp_id(request: Request) -> str:
    """从请求中获取RP ID（域名）"""
    host = request.headers.get("host", "localhost").split(":")[0]
    # 如果是IP地址，使用localhost
    if host.replace(".", "").isdigit():
        return "localhost"
    return host

@router.post("/register/begin")
async def webauthn_register_begin(
    request: Request,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    开始注册WebAuthn凭证
    用户需要已登录，且每个用户只能绑定一个密钥
    """
    try:
        # 检查是否已绑定密钥
        if current_user.is_webauthn_enabled:
            return fail("您已经绑定了硬件密钥，请先解绑后再绑定新密钥")
        
        # 生成challenge
        challenge = generate_challenge()
        
        # 存储challenge（关联到用户）
        _challenges[f"reg_{current_user.id}"] = {
            "challenge": challenge,
            "timestamp": datetime.utcnow()
        }
        
        # 获取当前域名
        rp_id = get_rp_id(request)
        
        # 构建WebAuthn注册选项
        options = {
            "challenge": challenge,
            "rp": {
                "name": "UserMails",
                "id": rp_id
            },
            "user": {
                "id": base64.urlsafe_b64encode(str(current_user.id).encode()).decode('utf-8').rstrip('='),
                "name": current_user.username,
                "displayName": current_user.full_name or current_user.username
            },
            "pubKeyCredParams": [
                {"type": "public-key", "alg": -7},  # ES256
                {"type": "public-key", "alg": -257}  # RS256
            ],
            "timeout": 60000,
            "attestation": "none",
            "authenticatorSelection": {
                "authenticatorAttachment": "cross-platform",
                "residentKey": "required",  # 要求驻留凭证
                "userVerification": "preferred"
            },
            "extensions": {
                "credProps": True
            }
        }
        
        return success({"options": options})
    except Exception as e:
        return fail(f"生成注册选项失败: {str(e)}")

@router.post("/register/complete")
async def webauthn_register_complete(
    request: WebAuthnRegisterCompleteRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    完成WebAuthn凭证注册
    """
    try:
        # 再次检查是否已绑定
        if current_user.is_webauthn_enabled:
            return fail("您已经绑定了硬件密钥")
        
        # 验证challenge
        challenge_key = f"reg_{current_user.id}"
        if challenge_key not in _challenges:
            return fail("注册会话已过期，请重新开始")
        
        stored_data = _challenges[challenge_key]
        
        # 检查challenge是否过期（5分钟）
        if (datetime.utcnow() - stored_data["timestamp"]).seconds > 300:
            del _challenges[challenge_key]
            return fail("注册会话已过期，请重新开始")
        
        # 提取凭证数据
        credential_data = request.credential
        credential_id = credential_data.get("id")
        response_data = credential_data.get("response", {})
        
        if not credential_id or not response_data:
            return fail("凭证数据不完整")
        
        # 提取公钥
        client_data_json = response_data.get("clientDataJSON")
        attestation_object = response_data.get("attestationObject")
        
        if not client_data_json or not attestation_object:
            return fail("凭证响应数据不完整")
        
        # 验证clientDataJSON中的challenge
        try:
            client_data = json.loads(base64.urlsafe_b64decode(client_data_json + "=="))
            if client_data.get("challenge") != stored_data["challenge"]:
                return fail("Challenge验证失败")
        except Exception as e:
            return fail(f"验证凭证数据失败: {str(e)}")
        
        # 保存凭证到用户记录
        current_user.webauthn_credential_id = credential_id
        current_user.webauthn_public_key = attestation_object
        current_user.webauthn_sign_count = 0
        current_user.is_webauthn_enabled = True
        
        db.commit()
        db.refresh(current_user)
        
        # 清除challenge
        del _challenges[challenge_key]
        
        return success(msg="硬件密钥绑定成功")
    except Exception as e:
        db.rollback()
        return fail(f"绑定密钥失败: {str(e)}")

@router.post("/login/begin")
async def webauthn_login_begin(
    http_request: Request,
    db: Session = Depends(get_db)
):
    """
    开始WebAuthn登录（无用户名，使用驻留凭证）
    """
    try:
        # 生成challenge
        challenge = generate_challenge()
        
        # 获取当前域名
        rp_id = get_rp_id(http_request)
        
        # 使用特殊key存储challenge
        _challenges[f"login_discoverable_{challenge}"] = {
            "challenge": challenge,
            "timestamp": datetime.utcnow()
        }
        
        # 构建无凭证限制的登录选项（允许使用任何驻留凭证）
        options = {
            "challenge": challenge,
            "timeout": 60000,
            "rpId": rp_id,
            "userVerification": "preferred"
            # 不设置allowCredentials，让浏览器显示所有可用凭证
        }
        
        return success({"options": options})
    except Exception as e:
        return fail(f"生成登录选项失败: {str(e)}")

@router.post("/login/complete")
async def webauthn_login_complete(
    request: WebAuthnLoginCompleteRequest,
    db: Session = Depends(get_db)
):
    """
    完成WebAuthn登录（通过凭证ID自动识别用户）
    """
    try:
        # 提取凭证数据
        credential_data = request.credential
        credential_id = credential_data.get("id")
        response_data = credential_data.get("response", {})
        
        if not credential_id or not response_data:
            return fail("凭证数据不完整")
        
        # 验证clientDataJSON中的challenge
        try:
            client_data_json = response_data.get("clientDataJSON")
            client_data = json.loads(base64.urlsafe_b64decode(client_data_json + "=="))
            challenge = client_data.get("challenge")
        except Exception as e:
            return fail(f"验证凭证数据失败: {str(e)}")
        
        # 根据凭证ID查找用户
        user = db.query(User).filter(User.webauthn_credential_id == credential_id).first()
        
        if not user:
            return fail("未找到匹配的用户凭证")
        
        # 检查账号状态
        if not user.is_active:
            return fail("账号已被禁用")
        
        if user.delete_time is not None:
            return fail("账号已被删除")
        
        # 验证challenge
        discoverable_key = f"login_discoverable_{challenge}"
        
        if discoverable_key not in _challenges:
            return fail("登录会话已过期，请重新开始")
        
        stored_data = _challenges[discoverable_key]
        
        # 检查challenge是否过期（5分钟）
        if (datetime.utcnow() - stored_data["timestamp"]).seconds > 300:
            del _challenges[discoverable_key]
            return fail("登录会话已过期，请重新开始")
        
        # 验证challenge
        if challenge != stored_data["challenge"]:
            return fail("Challenge验证失败")
        
        # 更新签名计数器
        user.webauthn_sign_count += 1
        db.commit()
        
        # 清除challenge
        del _challenges[discoverable_key]
        
        # 生成JWT token
        access_token_expires = timedelta(minutes=60)
        access_token = create_access_token(
            data={"sub": user.username}, expires_delta=access_token_expires
        )
        
        return success({
            "access_token": access_token,
            "token_type": "bearer"
        }, msg="登录成功")
    except Exception as e:
        db.rollback()
        return fail(f"登录失败: {str(e)}")

@router.get("/status")
async def get_webauthn_status(
    current_user: User = Depends(get_current_user)
):
    """
    获取当前用户的硬件密钥状态
    """
    try:
        return success({
            "is_webauthn_enabled": current_user.is_webauthn_enabled,
            "has_credential": bool(current_user.webauthn_credential_id)
        })
    except Exception as e:
        return fail(f"获取状态失败: {str(e)}")

@router.post("/unbind")
async def unbind_webauthn(
    request: WebAuthnUnbindRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    解绑硬件密钥（需要验证令牌）
    """
    try:
        if not current_user.is_webauthn_enabled:
            return fail("您还未绑定硬件密钥")
        
        # 验证验证令牌
        if not verify_verification_token(request.verification_token, current_user.id):
            return fail("验证令牌无效或已过期")
        
        # 清除密钥信息
        current_user.webauthn_credential_id = None
        current_user.webauthn_public_key = None
        current_user.webauthn_sign_count = 0
        current_user.is_webauthn_enabled = False
        
        db.commit()
        
        # 使验证令牌失效
        invalidate_verification_token(request.verification_token)
        
        return success(msg="硬件密钥已解绑")
    except Exception as e:
        db.rollback()
        return fail(f"解绑失败: {str(e)}")

# ==================== 管理员接口 ====================

@router.post("/admin/users/{user_id}/unbind")
async def admin_unbind_user_webauthn(
    user_id: int,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """
    管理员解绑用户的硬件密钥
    """
    try:
        if not current_user.is_admin:
            return fail("权限不足")
        
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            return fail("用户不存在")
        
        if not user.is_webauthn_enabled:
            return fail("该用户未绑定硬件密钥")
        
        # 清除密钥信息
        user.webauthn_credential_id = None
        user.webauthn_public_key = None
        user.webauthn_sign_count = 0
        user.is_webauthn_enabled = False
        
        db.commit()
        
        return success(msg="硬件密钥已解绑")
    except Exception as e:
        db.rollback()
        return fail(f"解绑失败: {str(e)}")